package com.yc.navy.springmvc.core;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;



import com.yc.navy.springmvc.core.annotation.Autowired;
import com.yc.navy.springmvc.core.annotation.Component;
import com.yc.navy.springmvc.core.annotation.Controller;
import com.yc.navy.springmvc.core.annotation.GetMapping;
import com.yc.navy.springmvc.core.annotation.PostMapping;
import com.yc.navy.springmvc.core.annotation.Repository;
import com.yc.navy.springmvc.core.annotation.RequestMapping;
import com.yc.navy.springmvc.core.annotation.RestController;
import com.yc.navy.springmvc.core.annotation.Service;
import com.yc.navy.springmvc.util.StringUtil;

public class SpringFrameworkCore {
	private String contextConfigLocation;
	private String basePackage;
	private Set<String> classNames = new CopyOnWriteArraySet<String>();
	private Map<String, Object> instanceObject = new Hashtable<String, Object>(); // 存放实例化好的对象
	private Map<String, HandlerMapperInfo> handlerMapper = new Hashtable<String, HandlerMapperInfo>();
	public SpringFrameworkCore(String contextConfigLocation) {
		this.contextConfigLocation = contextConfigLocation;
		init();	//开始解析
	}

	private void init() {
		//1、读取配置文件，然后获取要扫描的基址路径 ->	传配置文件路径
		doLoadConfig();
		//2、扫描包，获取配置路径下的所有类
		doScannerPackage();
		//3、初始化IoC容器，实例化需要IoC容器管理的类：有Controller、RestController、Component、Service、Repository
		doInstanceObject();
		
		//4、执行依赖注入，完成@Autowired注解，先根据类型，
		try {
			doAutowired();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		//5、构建HandlerMapping,完成请求地址URI与对应方法之间的关联映射
		doHandlerMapping();
		
	}


	//1、读取配置文件，然后获取要扫描的基址路径 ->	传配置文件路径
	private void doLoadConfig() {
		try(InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation)){
			Properties prop = new Properties();
			prop.load(is);
			basePackage = prop.getProperty("basePackage");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 2、扫描包，获取配置路径下的所有类
	 */
	private void doScannerPackage() {
		if(basePackage == null || basePackage.isEmpty()) {
			throw new RuntimeException("读取配置文件失败,请配置contextConfigLocation参数以及basePackage属性...");
		}
		
		URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
		String path = url.getPath();
		
		try {
			path = URLDecoder.decode(path, "utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		File desc = new File(path);
	}

	/**
	 * 扫描指定包路径的
	 * @throws Exception 
	 */
	private void getClassInfo(String basePackage, File desc) {
		if(desc.exists() && desc.isDirectory() && !basePackage.contains(".annotation")) {
			for(File f1 : desc.listFiles()) {
				if(f1.isDirectory()) {
					getClassInfo(basePackage + "." + f1.getName(), f1);
				}
				classNames.add(basePackage + "." + f1.getName().replace(".class", ""));
			}
		}
	}
	
	
	/**
	 * 3.初始化IoC容器对象
	 * @throws Exception 
	 */
	private void doInstanceObject() {
		if (classNames.isEmpty()) {
			return;
		}
		
		Class<?> cls= null;
		String beanName = null;
		
		for (String className : classNames) {
			try {
				cls = Class.forName(className);
			
				if (cls.isAnnotationPresent(Controller.class)) {
					beanName = cls.getAnnotation(Controller.class).value();
					if( beanName == null || beanName.isEmpty() ) {
						beanName = toFirstLowserName(cls.getSimpleName());
					}
					instanceObject.put(beanName, cls.getConstructor().newInstance());
					continue;
				} 
				if (cls.isAnnotationPresent(RestController.class)) {
					beanName = cls.getAnnotation(RestController.class).value();
					if( beanName == null || beanName.isEmpty() ) {
						beanName = toFirstLowserName(cls.getSimpleName());
					}
					instanceObject.put(beanName, cls.getConstructor().newInstance());
					continue;
				}
				if (cls.isAnnotationPresent(Service.class)) {
					beanName = cls.getAnnotation(Service.class).value();
					if( beanName == null || beanName.isEmpty() ) {
						beanName = toFirstLowserName(cls.getSimpleName());
					}
					instanceObject.put(beanName, cls.getConstructor().newInstance());
					continue;
				}
				if (cls.isAnnotationPresent(Repository.class)) {
					beanName = cls.getAnnotation(Repository.class).value();
					if( beanName == null || beanName.isEmpty() ) {
						beanName = toFirstLowserName(cls.getSimpleName());
					}
					instanceObject.put(beanName, cls.getConstructor().newInstance());
					continue;
				}
				if (cls.isAnnotationPresent(Component.class)) {
					beanName = cls.getAnnotation(Component.class).value();
					if( beanName == null || beanName.isEmpty() ) {
						beanName = toFirstLowserName(cls.getSimpleName());
					}
					instanceObject.put(beanName, cls.getConstructor().newInstance());
					continue;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 首字母小写
	 * @param name
	 * @return
	 */
	private String toFirstLowserName(String name) {
		return name.substring(0, 1).toLowerCase() + name.substring(1);
	}
	
	/**
	 * 依赖注入
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 */
	private void doAutowired() throws IllegalArgumentException, IllegalAccessException {
		if(instanceObject.isEmpty()) {
			return;
		}
		
		Field[] fields = null;
		Class<?> cls = null;
		Autowired awd = null;
		String beanName = null;
		Class<?> typeCls = null;
		Object obj = null;
		Collection<Object> objs = instanceObject.values(); // 取出Ioc容器中所有的对象
		List<Object> list = new ArrayList<Object>();

		for(Entry<String, Object> entry : instanceObject.entrySet()) {
			cls = entry.getValue().getClass();
			fields = cls.getDeclaredFields();	//获取这个对象类型下的所有属性
			if (fields == null || fields.length <= 0) {
				continue;
			}
			
			for(Field fd : fields) {
				if(!fd.isAnnotationPresent(Autowired.class)) {//说明这个属性不需要注值
					continue;
				}
				
				fd.setAccessible(true);	//因为属性私有，私有需要先开启权限
				awd = fd.getAnnotation(Autowired.class);
				beanName = awd.value();//获取指定要注入的bean名称
				// 判断是否有配置需要注入的bean的名字
				if(!StringUtil.checkNull(beanName)) { // 说明有指定需要注入的bean name
					// 先取出要注入的对象
					obj = instanceObject.get(beanName);	//根据bean的名称从IoC容器中获取要注入的对象
					if (obj == null) {
						throw new RuntimeException(cls.getName() + "." + fd.getName() + "注值失败，没有找到对应的bean " + beanName);
					}
					fd.set(entry.getValue(), obj); // 属性名.set(这个属性所属的对象，这个属性要注入的值)
					continue;
				}
				
				typeCls = fd.getType();	//获取属性的类型，然后从IoC容器中找出所有满足这个类型要求的对象
				for(Object valobj : objs) {
					if(valobj.getClass() == typeCls) {
						list.add(valobj);
						continue;
					}
					for(Class<?> c : obj.getClass().getInterfaces()) {
						if(c == typeCls) {
							list.add(obj);
							continue;
						}
					}
				}
				if (list.isEmpty()) { // 说明没有这个类型
					throw new RuntimeException(cls.getName() + "." + fd.getName() + " 注值失败，没有找到对应类型的bean " + beanName);
				}
				
				if (list.size() == 1){
					fd.set(entry.getValue(), list.get(0));
					continue;
				}
				
				if (list.size() > 1) {
					beanName = fd.getName(); // 获取这个字段的名称
					obj = instanceObject.get(beanName);
					if(obj == null) { // 说明根据名字注入也不行
						throw new RuntimeException(cls.getName() + "." + fd.getName() + "注值失败， 有" + list.size() + " bean满足条件["+ list + "]"); 
					}
					fd.set(entry.getValue(), obj);
				}
			}
		}
	}
	/**
	 * 构建HandlerMapping,完成请求地址URI与对应方法之间的关联映射
	 */
	private void doHandlerMapping() {
		if(instanceObject.isEmpty()) {
			return;
		}
		
		String baseUrl = ""; //存类上的路径
		String url;	//存方法上的路径
		Method[] methods;
		Class<?> cls = null;
		RequestMapping requestMapping;
		String requestMethod = "";	//存请求的方法
		
		for(Entry<String, Object> entry : instanceObject.entrySet()) {
			baseUrl = "";
			requestMethod = "";
			cls = entry.getValue().getClass();
			
			if(!cls.isAnnotationPresent(Controller.class) && !cls.isAnnotationPresent(RestController.class)) {
				continue;
			}
			
			requestMapping = cls.getAnnotation(RequestMapping.class);
			if(requestMapping != null) {
				baseUrl = requestMapping.value();
				if(!baseUrl.startsWith("/")) {
					baseUrl = "/" + baseUrl;
				}
			}
			
			//获取这个类下的所有的方法
			methods = cls.getDeclaredMethods();
			if(methods == null || methods.length <= 0) {
				continue;
			}
			
			for(Method method : methods) {
				if(method.isAnnotationPresent(RequestMapping.class)) {
					url = method.getAnnotation(RequestMapping.class).value();
					requestMethod = "";
				} else if(method.isAnnotationPresent(PostMapping.class)) {
					url = method.getAnnotation(PostMapping.class).value();
					requestMethod = "POST";
				} else if(method.isAnnotationPresent(GetMapping.class)) {
					url = method.getAnnotation(GetMapping.class).value();
					requestMethod = "GET";
				} else {
					continue;
				}
				if(!url.startsWith("/")) {
					url = "/" + url;
				}
				
				url = baseUrl + url;
				handlerMapper.put(url.replaceAll("/+", "/"), new HandlerMapperInfo(method, entry.getValue(), requestMethod));
			}
		}
	}				


	public String getContextConfigLocation() {
		return contextConfigLocation;
	}

	public String getBasePackage() {
		return basePackage;
	}

	public Set<String> getClassNames() {
		return classNames;
	}

	public Map<String, Object> getInstanceObject() {
		return instanceObject;
	}

	public Map<String, HandlerMapperInfo> getHandlerMapper() {
		return handlerMapper;
	}
	
	public HandlerMapperInfo getMapper(String url) {
		return handlerMapper.get(url);
	}

}
